package com.example.devsa_platform.service.impl;

import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.aggregations.Buckets;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import com.example.devsa_platform.common.Result;
import com.example.devsa_platform.pojo.doc.TechCultureDoc;
import com.example.devsa_platform.pojo.vo.stats.TechCultureStatsVo;
import com.example.devsa_platform.service.TechCultureService;
import jakarta.annotation.Resource;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregations;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class TechCultureServiceImpl implements TechCultureService {

    @Resource
    private ElasticsearchTemplate template;

    @Override
    public Result<TechCultureStatsVo> techCultureInfoStats() {
        NativeQuery query = NativeQuery.builder()
                .withAggregation("languageHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("language_have_worked_with.keyword")))
                )
                .withAggregation("languageWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("language_want_to_work_with.keyword")))
                )
                .withAggregation("databaseHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("database_have_worked_with.keyword")))
                )
                .withAggregation("databaseWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("database_want_to_work_with.keyword")))
                )
                .withAggregation("platformHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("platform_have_worked_with.keyword")))
                )
                .withAggregation("platformWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("platform_want_to_work_with.keyword")))
                )
                .withAggregation("webframeHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("webframe_have_worked_with.keyword")))
                )
                .withAggregation("webframeWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("webframe_want_to_work_with.keyword")))
                )
                .withAggregation("miscTechHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("misc_tech_have_worked_with.keyword")))
                )
                .withAggregation("miscTechWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("misc_tech_want_to_work_with.keyword")))
                )
                .withAggregation("toolsTechHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("tools_tech_have_worked_with.keyword")))
                )
                .withAggregation("toolsTechWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("tools_tech_want_to_work_with.keyword")))
                )
                .withAggregation("newCollabToolsHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("new_collab_tools_have_worked_with.keyword")))
                )
                .withAggregation("newCollabToolsWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("new_collab_tools_want_to_work_with.keyword")))
                )
                .withAggregation("opSysPersonalUseGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("op_sys_personal_use.keyword")))
                )
                .withAggregation("opSysProfessionalUseGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("op_sys_professional_use.keyword")))
                )
                .withAggregation("officeStackAsyncHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("office_stack_async_have_worked_with.keyword")))
                )
                .withAggregation("officeStackAsyncWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("office_stack_async_want_to_work_with.keyword")))
                )
                .withAggregation("officeStackSyncHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("office_stack_sync_have_worked_with.keyword")))
                )
                .withAggregation("officeStackSyncWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("office_stack_sync_want_to_work_with.keyword")))
                )
                .withAggregation("aiSearchHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("ai_search_have_worked_with.keyword")))
                )
                .withAggregation("aiSearchWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("ai_search_want_to_work_with.keyword")))
                )
                .withAggregation("aiDevHaveWorkedWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("ai_dev_have_worked_with.keyword")))
                )
                .withAggregation("aiDevWantToWorkWithGroup", Aggregation.of(ag -> ag
                        .terms(te -> te.field("ai_dev_want_to_work_with.keyword")))
                )
                .build();

        AggregationsContainer<?> aggregationsContainer = template.search(query, TechCultureDoc.class).getAggregations();
        TechCultureStatsVo techCultureStatsVo = null;

        if (aggregationsContainer instanceof ElasticsearchAggregations elasticsearchAggregations) {
            List<Map<String, Object>> languageHaveWorkedWithGroup = parseAndAggregateResults("languageHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> languageWantToWorkWithGroup = parseAndAggregateResults("languageWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> databaseHaveWorkedWithGroup = parseAndAggregateResults("databaseHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> databaseWantToWorkWithGroup = parseAndAggregateResults("databaseWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> platformHaveWorkedWithGroup = parseAndAggregateResults("platformHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> platformWantToWorkWithGroup = parseAndAggregateResults("platformWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> webframeHaveWorkedWithGroup = parseAndAggregateResults("webframeHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> webframeWantToWorkWithGroup = parseAndAggregateResults("webframeWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> miscTechHaveWorkedWithGroup = parseAndAggregateResults("miscTechHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> miscTechWantToWorkWithGroup = parseAndAggregateResults("miscTechWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> toolsTechHaveWorkedWithGroup = parseAndAggregateResults("toolsTechHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> toolsTechWantToWorkWithGroup = parseAndAggregateResults("toolsTechWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> newCollabToolsHaveWorkedWithGroup = parseAndAggregateResults("newCollabToolsHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> newCollabToolsWantToWorkWithGroup = parseAndAggregateResults("newCollabToolsWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> opSysPersonalUseGroup = parseAndAggregateResults("opSysPersonalUseGroup", elasticsearchAggregations);
            List<Map<String, Object>> opSysProfessionalUseGroup = parseAndAggregateResults("opSysProfessionalUseGroup", elasticsearchAggregations);
            List<Map<String, Object>> officeStackAsyncHaveWorkedWithGroup = parseAndAggregateResults("officeStackAsyncHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> officeStackAsyncWantToWorkWithGroup = parseAndAggregateResults("officeStackAsyncWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> officeStackSyncHaveWorkedWithGroup = parseAndAggregateResults("officeStackSyncHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> officeStackSyncWantToWorkWithGroup = parseAndAggregateResults("officeStackSyncWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> aiSearchHaveWorkedWithGroup = parseAndAggregateResults("aiSearchHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> aiSearchWantToWorkWithGroup = parseAndAggregateResults("aiSearchWantToWorkWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> aiDevHaveWorkedWithGroup = parseAndAggregateResults("aiDevHaveWorkedWithGroup", elasticsearchAggregations);
            List<Map<String, Object>> aiDevWantToWorkWithGroup = parseAndAggregateResults("aiDevWantToWorkWithGroup", elasticsearchAggregations);

            techCultureStatsVo = new TechCultureStatsVo(
                    languageHaveWorkedWithGroup,
                    languageWantToWorkWithGroup,
                    databaseHaveWorkedWithGroup,
                    databaseWantToWorkWithGroup,
                    platformHaveWorkedWithGroup,
                    platformWantToWorkWithGroup,
                    webframeHaveWorkedWithGroup,
                    webframeWantToWorkWithGroup,
                    miscTechHaveWorkedWithGroup,
                    miscTechWantToWorkWithGroup,
                    toolsTechHaveWorkedWithGroup,
                    toolsTechWantToWorkWithGroup,
                    newCollabToolsHaveWorkedWithGroup,
                    newCollabToolsWantToWorkWithGroup,
                    opSysPersonalUseGroup,
                    opSysProfessionalUseGroup,
                    officeStackAsyncHaveWorkedWithGroup,
                    officeStackAsyncWantToWorkWithGroup,
                    officeStackSyncHaveWorkedWithGroup,
                    officeStackSyncWantToWorkWithGroup,
                    aiSearchHaveWorkedWithGroup,
                    aiSearchWantToWorkWithGroup,
                    aiDevHaveWorkedWithGroup,
                    aiDevWantToWorkWithGroup
            );
        }
        return Result.success(techCultureStatsVo);
    }

    private List<Map<String, Object>> parseAndAggregateResults(String aggregationName, ElasticsearchAggregations elasticsearchAggregations) {
        Map<String, Long> aggregatedCounts = new HashMap<>();

        List<Map<String, Object>> originalResults = parseAggregation(aggregationName, elasticsearchAggregations);
        for (Map<String, Object> result : originalResults) {
            String key = (String) result.get("key");
            Long docCount = ((Number) result.get("doc_count")).longValue();

            // Split the key by ";"
            String[] parts = key.split(";");
            for (String part : parts) {
                part = part.trim();
                aggregatedCounts.put(part, aggregatedCounts.getOrDefault(part, 0L) + docCount);
            }
        }

        // Convert aggregatedCounts to the desired output format
        List<Map<String, Object>> parsedResults = new ArrayList<>();
        for (Map.Entry<String, Long> entry : aggregatedCounts.entrySet()) {
            Map<String, Object> map = new HashMap<>();
            map.put("key", entry.getKey());
            map.put("doc_count", entry.getValue());
            parsedResults.add(map);
        }

        return parsedResults;
    }

    private List<Map<String, Object>> parseAggregation(String aggName, ElasticsearchAggregations elasticsearchAggregations) {
        Buckets<StringTermsBucket> buckets = Objects.requireNonNull(elasticsearchAggregations.get(aggName)).aggregation().getAggregate().sterms().buckets();
        return buckets.array().stream()
                .map(bucket -> {
                    String key = bucket.key().stringValue();
                    long docCount = bucket.docCount();
                    Map<String, Object> map = new HashMap<>();
                    map.put("key", key);
                    map.put("doc_count", docCount);
                    return map;
                })
                .collect(Collectors.toList());
    }
}
